Skip to content

Make nix-daemon.sh idempotent#15005

Open
convoliution wants to merge 7 commits intoNixOS:masterfrom
convoliution:nix-profile-daemon-idempotent
Open

Make nix-daemon.sh idempotent#15005
convoliution wants to merge 7 commits intoNixOS:masterfrom
convoliution:nix-profile-daemon-idempotent

Conversation

@convoliution
Copy link

@convoliution convoliution commented Jan 16, 2026

Motivation

This change makes nix-daemon.sh idempotent, enabling any interactive shell to ensure it has its PATH correctly configured for Nix-managed executables.

Context

When a login shell starts an interactive shell, the latter is unable to access nix and Nix-managed executables, or they are out-precedented by system executables. This leads to issues in tmux (issue #13255), SteamOS (issue #13355), and in my own experience the terminal in VS Code.

This is because when the former shell executes nix-daemon.sh (at the beginning of e.g., /etc/zshrc), it exports a flag indicating the script has been run:

export __ETC_PROFILE_NIX_SOURCED=1

before modifying PATH to give precedence to Nix-managed executables:

export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"

However, the latter shell inherits __ETC_PROFILE_NIX_SOURCED, and so skips the entirety of nix-daemon.sh:

if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi

thus missing out on the PATH modification.

Implementation Strategy

This change first filters out possible duplicate Nix entries from PATH (and XDG_DATA_DIRS while we're at it) by

  1. wrapping the full variable in :s for ease of processing,
  2. removing constituent :$NIX_ENTRY:s (replacing them with :s),
  3. unwrapping the start and end :s from (1),

and then finally prepending/appending the Nix entries as appropriate.

One possible issue is that since I'm using sed, there may be edge cases involving regex metacharacters, but I think they're astronomically unlikely to actually cause a problem.

Validation

I tried to access https://nix.dev/manual/nix/development/development/building.html but Anubis fails to load for me 🤖, so instead I just directly manually overwrote my local /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh

Before

Following tmux's behavior of creating login shells for new windows (tmux-users mailing list), which VS Code also does by default for its integrated terminal (source):

% echo $PATH
/Users/convoliution/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Users/convoliution/.local/bin
% zsh -li          
% echo $PATH
/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Users/convoliution/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/Users/convoliution/.local/bin

Entries are appended ahead of /Users/convoliution/.nix-profile/bin:/nix/var/nix/profiles/default/bin. This happens because on macOS, /etc/zprofile (which runs before /etc/zshrc) modifies PATH by calling the path_helper utility (UNIX StackExchange). An analogous modification to PATH occurs on Debian (issue #13255).

After

% echo $PATH
/Users/convoliution/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Users/convoliution/.local/bin
% zsh -li
% echo $PATH
/Users/convoliution/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pmk/env/global/bin:/Users/convoliution/.local/bin

And to verify that it's idempotent for the four environment variables it sets, barring changes to the user's profile or certificates:

% PREV_NIX_PROFILES="$NIX_PROFILES"
% PREV_XDG_DATA_DIRS="$XDG_DATA_DIRS"
% PREV_NIX_SSL_CERT_FILE="$NIX_SSL_CERT_FILE"
% PREV_PATH="$PATH"
% source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
% [[ "$NIX_PROFILES" == "$PREV_NIX_PROFILES" ]]; echo $?
0
% [[ "$XDG_DATA_DIRS" == "$PREV_XDG_DATA_DIRS" ]]; echo $?
0
% [[ "$NIX_SSL_CERT_FILE" == "$PREV_NIX_SSL_CERT_FILE" ]]; echo $?
0
% [[ "$PATH" == "$PREV_PATH" ]]; echo $?
0

Alternative Approaches

This is an alternative to PR #14697, which would revert PR #12805 by allowing nix-daemon.sh to be run multiple times but resulting in duplicate entries prepended to PATH (issue #5950).

I felt there were two conflicting ideas here. PR #12805 (and the open PR #14922 for nix.sh) which exports the flag is certainly a bug fix and desired behavior; nix-daemon.sh has always stated:

# Only execute this file once per shell.

But the purpose and intended postcondition of nix-daemon.sh is clearly, among other things, to have Nix's */bin directories at the beginning of PATH:

export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH"

which PR #14697 guarantees.

By making nix-daemon.sh idempotent, we can have our script and run it too 🍰.


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants